package com.demo.bmsconsole;

import com.demo.bmsconsole.State.InitWaitTimer;

import android.content.Context;
import android.os.CountDownTimer;
import android.util.Log;

public class State
{
	private static Context mCtx;
	private static State mSingleton;
	private static String TAG;
	
	// Overall status of controller.
	public enum EStatus {OK, TimedOut, Error};

	// Ideally, should do something more object-oriented,
	// but will do for now.
	
	// Major states: TODO
	// Meaning is that major states indicate what state the Controller is in.
	// Minor states indicate exact state.
	// Do we need a Ready state (Or User-Input)?
	public enum EMajor { None, Init, Forward, Reverse };

	// Minor states
	// InitState. Sticky. Once Initialization is done, it retains 
	// final value: ConfigDone
	public enum EInit { CommError(-1), CellCountError(-2), ConfigError(-3), 
						None(0), 
						CommOK(1), ConfigDone(2), CellCountOK(3); private final int val; EInit(int i) {this.val = i;}};
	
	// ForwardState
	public enum EForward {None, IOError, Gear1, Gear2, Gear3};
	
	//ReverseState
	public enum EReverse {None, IOError, Gear1};

	public static EStatus Status;
	public static EMajor  MajorState;
	public static EInit  Init;
	public static EForward Forward;
	public static EReverse Reverse;
	private static InitWaitTimer mTimer;
	
	State(Context ctx)
	{
		mCtx = ctx;
		TAG = ctx.getPackageName();
		mSingleton = this;
		initialize();
	}

	// Timer to ensure we complete initialization.
	public class InitWaitTimer extends CountDownTimer {
		public InitWaitTimer() {
			super(Config.InitTimeout,1000);
		}
		public void onTick(long millisUntilFinished) {
		}
		public void onFinish() {
			// Notify State.
			State.initTimerFinished();
		}
	}

	
    public void initialize()
	{
		Status = EStatus.OK;
		MajorState = EMajor.None;
		Init = EInit.None;
		Forward = EForward.None;
		Reverse = EReverse.None;
		nextState();
	}

	public static void setMajorState(EMajor state)
	{
		MajorState = state;
		switch(state) {
		case Init:
			Init = EInit.None;
			break;
		case Forward:
			Forward = EForward.None;
		case Reverse:
			Reverse = EReverse.None;
		}
		nextState();
	}

	public static void setCommStatus(boolean ok) {
		if (MajorState == EMajor.Init && Init == EInit.None) {
			setInitState(ok?EInit.CommOK:EInit.CommError);
		} else {
			LogFile.log("Unexpected State Change: In:" + 
					currentState() + "Rcvd: CommStatus " + 
					(ok?"OK":"Error"));
		}
	}

	public static void setCellCount(int count) {
		if (MajorState == EMajor.Init && Init == EInit.CommOK) {
			if (count == Config.CellCount) {
				setInitState(EInit.CellCountOK);
			} else {
				setInitState(EInit.CellCountError);
				String logLine = "CellCount Mismatch. Expected/Rcvd = " + Config.CellCount + "/" + count; 
				LogFile.log(logLine);
				Log.w(TAG, logLine);
			}
		} else {
			LogFile.log("Unexpected State Change: In:" + 
					currentState() + "Rcvd: CellCount");
		}
	}
		
	// 
	public static void initTimerFinished()
	{
		// make sure we're not done.
		if (Init != EInit.CellCountOK) {
			Status = EStatus.TimedOut;
			

		}
	}
	public static boolean isInitDone()
	{
		return (Init == EInit.CellCountOK);
	}

	public static boolean isTimedOut() {
		return Status == EStatus.TimedOut;
	}

	public static boolean isError() {
		return Status == EStatus.Error;
	}

	private static String currentState()
	{
		String ret = "";
		switch (MajorState) {
		case Init:
			ret = "Init-";
			switch(Init) {
			case None:
				ret += "None";
				break;
			case CommOK:
				ret += "CommOK";
				break;
			case CommError:
				ret += "CommError";
				break;
			case CellCountOK:
				ret += "CellCountOK";
				break;
			case CellCountError:
				ret += "CellCountError";
				break;
			case ConfigDone:
				ret += "ConfigDone";
				break;
			case ConfigError:
				ret += "ConfigError";
				break;
			default:
				ret += "Corrupted!";
			}
			break;
		case Forward:
			ret = "Forward-";
			switch(Forward) {
			case None:
				ret += "None";
				break;
			case IOError:
				ret += "IOError";
				break;
			case Gear1:
				ret += "Gear1";
				break;
			case Gear2:
				ret += "Gear2";
				break;
			case Gear3:
				ret += "Gear3";
				break;
			default:
				ret += "Corrupted!";
			}
			break;
		case Reverse:
			ret = "Reverse-";
			switch(Reverse) {
			case None:
				ret += "None";
				break;
			case IOError:
				ret += "IOError";
				break;
			case Gear1:
				ret += "Gear1";
				break;
			default:
				ret += "Corrupted!";
			}
			break;
		case None:
			ret = "None";
		}
		return ret;
	}
	
	/*
	public enum EMajor { None, Init, Forward, Reverse };
	public enum EInit { None, CommOK, CommError, ConfigDone, ConfigError, CellCountOK, CellCountError };
	public enum EForward {None, IOError, Gear1, Gear2, Gear3};
	public enum EReverse {None, IOError, Gear1};
	*/

	// State Machine: Initiate actions to get to the next state
	private static void nextState() 
	{
		switch (MajorState) {
		case Init:
			nextInitState();
			break;
		case Forward:
			nextForwardState();
			break;
		case Reverse:
			nextReverseState();
			break;
		case None:
		}
	}
	
	// Reset Init state and call BMS console.
	private static void initFailed(boolean timedOut)
	{
		Status = timedOut?EStatus.TimedOut:EStatus.Error;
		BMSconsole.initComplete(false);
	}
	
	private static void nextInitState()
	{
		switch(Init) {
		case None:
			// initialization should complete by a certain time.
			mTimer = mSingleton.new InitWaitTimer();
			ADBServer.run();
			break;
		case CommOK:
			// have to initiate configuration-sets
			if (Interpreter.setControllerConfig()) {
				Init = EInit.ConfigDone;
			} else {
				Init = EInit.ConfigError;
			}
			break;
		case CommError:
			// Need to prompt user to reset.
			initFailed(false);
			break;
		case ConfigDone:
			Interpreter.beginI2Cinit();
			// nothing to do but wait for response.
			break;
		case CellCountOK:
			// cancel timer and inform BMSconsole
			mTimer.cancel();
			BMSconsole.initComplete(true);
			break;
		case CellCountError:
			// fall-thru
		case ConfigError:
			initFailed(false);
			break;
		}
	}
	
	private static void nextForwardState()
	{
		switch(Forward) {
		case None:
			break;
		case IOError:
			break;
		case Gear1:
			break;
		case Gear2:
			break;
		case Gear3:
			break;
		default:
		}
	}

	private static void nextReverseState()
	{
		switch(Forward) {
		case None:
			break;
		case IOError:
			break;
		case Gear1:
			break;
		default:
		}
	}

	private static void setInitState(EInit state)
	{
		switch(state) {
		case CommError:
		case CellCountError:
		case ConfigError:
			Status = EStatus.Error;
		}
		Init = state;
		nextState();
	}

	
}